package com.cskaoyan.javase.object._6clone;

/**
 * clone方法:
 * 1.方法的声明:  protected native Object clone() throws CloneNotSupportedException;
 *      它是一个本地方法
 *      首先访问权限是protected:
 *          同包下任意访问
 *          不同包下可以在子类中创建子类自身对象,然后访问
 *          当然子类中可以选择重写访问权限
 *      返回值类型是一个Object对象
 *
 * 2.方法的作用:
 *      这个方法的名字，其实已经很明显的告诉了你它的作用：
 * 克隆，生物学意义上的克隆，是用生物的体细胞，经过无性繁殖，得到相同基因型后代的过程。
 * Java当中的克隆方法clone()有类似的作用，当你在程序中有这种需求：
 *      希望得到一个完全独立的对象,并且成员(变量和方法)仍和原先对象一致。
 *
 * 普通的做法就是再new一个一模一样的，但学习clone方法后，你就可以用它来做这个是事情。 所以Object类当中的clone方法默认实现，就是得到一个独立的，和原先对象成员一致的新对象。
 *      Object类当中的clone方法的实现,是一个本地方法,依赖于这个本地方法创建对象
 *      它是一种完全独立于new对象的方式创建对象, 它们之间是平行的关系.
 *
 * 3.clone方法怎么用?
 *      a.首先就需要考虑访问权限, 默认情况下,我们只能在子类中创建子类自身对象才能够调用clone方法
 *          这意味着只能自己克隆自己,意义不大
 *          所以需要重写访问权限,这是做克隆操作的第一步.(必做)
 *
 *      b.一般情况下,我们直接使用clone方法在Object类中的默认实现就够了,不需要自己重写方法的实现.
 *          Object类中的默认实现,克隆后的对象,一定和原先对象的类型是一致的
 *          所以(选做)
 *          可以在子类中重写返回值类型,这样做方便后续的方法调用,推荐做一下.
 *
 *      c.(必做)Java语法设计中,要想对一个对象做克隆操作,那么这个对象的类型必须实现接口java.lang.Cloneable
 *      否者,做克隆操作会抛出异常CloneNotSupportedException
 *
 * 总结:
 *      要想对对象做克隆操作,首先需要考虑权限问题,一般需要重写方法访问权限,接下来还需要这个类实现接口Cloneable
 *      并且可以选做重写方法的返回值类型,因为Object类的clone默认实现一定会得到一个和原先对象类型一致的新对象.
 *
 * 细节问题(注意事项):
 *      1.clone方法创建对象和new对象是独立的,平行的创建对象的方式,互相没有关系.
 *              尤其注意clone方法创建对象,不需要使用构造器,它是依赖本地方法创建对象
 *      2.clone方法大多数情况下,没有重写的必要,使用Object类的实现足矣.
 *          但是如果想要重写,也是可以的,但总体需要遵守一定的原则:
 *          假如你真的有需求，对于某个对象的引用x，JDK文档中也规定了一些重写的原则：
 *              x.clone() != x 为 true  克隆后会得到一个新对象
 *              x.clone().getClass() == x.getClass() 一般也为true
 *                  克隆后的对象一般不会改变数据类型,但如有必要也可以.
 *              x.clone().equals(x) 一般情况下也为true (只有在使用成员变量取值重写equals方法后,这条才生效)
 *
 *      3.Cloneable接口,实际上它是一个空接口,里面没有任何成员,没有抽象方法.
 *          空接口的作用,实现Cloneable接口才能做克隆操作的原理
 *          空接口是一种标记接口,配合instanceof关键字使用,可以对其实现类和非实现类,做不同的操作
 *
 *      4.Java当中Object类的默认clone方法实现,是浅拷贝的,浅克隆的:
 *          1.对象中的基本数据类型成员变量,完全拷贝一份,互相不受影响
 *          2.对象中的引用数据类型成员变量,只会拷贝一份引用,这个拷贝引用和原先的引用指向同一个对象
 *          操作对象,会互相影响
 *
 *     和浅克隆相对应的就是深度克隆
 *     首先深度克隆需要在浅克隆的基础上完成
 *     并且需要把对象中引用数据类型引用指向的对象也克隆一份,让拷贝引用指向拷贝对象
 *     完成深度克隆
 *
 * @since 15:51
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        /*Student s1 = new Student(18, 100);
        // Unhandled exception: java.lang.CloneNotSupportedException
        Student cloneStu = s1.clone();

        *//*
            clone方法会得到一个完全独立的新对象
            和原先对象双等号连接返回false
         *//*
        System.out.println(s1 == cloneStu);
        System.out.println(s1.equals(cloneStu));*/


        Student s1 = new Student(18, 100, new Star(100000));
        Student cloneStu = s1.clone();
        s1.age = 28;
        System.out.println(cloneStu.age);
        s1.s.salary = 200000;
        System.out.println(cloneStu.s.salary);
    }
}

interface MyCloneable extends Cloneable {
}

class Student implements MyCloneable {
    int age;
    double score;
    Star s;


    {
        System.out.println("构造器执行了!");
    }

    public Student() {
    }

    public Student(int age, double score) {
        this.age = age;
        this.score = score;
    }

    public Student(int age, double score, Star s) {
        this.age = age;
        this.score = score;
        this.s = s;
    }

    // 重写clone方法的访问权限
    @Override
    protected Student clone() throws CloneNotSupportedException {
        // 现在对Student类做深度克隆
        // 1.深度克隆是在浅克隆基础上完成的
        Student cloneStu = (Student) super.clone();
        // 2.克隆一份Star对象
        Star cloneStar = cloneStu.s.clone();
        // 3.将拷贝对象中的拷贝s引用,指向拷贝Star引用
        cloneStu.s = cloneStar;
        return cloneStu;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.score, score) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

class Star implements Cloneable{
    double salary;

    public Star() {
    }

    public Star(double salary) {
        this.salary = salary;
    }

    // 首先解决访问权限问题,重写访问权限

    @Override
    protected Star clone() throws CloneNotSupportedException {
        return ((Star) super.clone());
    }
}